package com.hjh.usb.accessory.communication

import android.content.Context
import android.hardware.usb.UsbAccessory
import android.hardware.usb.UsbManager
import android.os.ParcelFileDescriptor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import java.io.FileDescriptor
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException

/**
 * 简单 AOA 通信
 */
abstract class UsbAccessoryCommunicator(
    context: Context
) {

    private val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
    private var fileDescriptor: ParcelFileDescriptor? = null
    private var inStream: FileInputStream? = null
    private var outStream: FileOutputStream? = null

    private val sendChannel = Channel<ByteArray>(capacity = Channel.UNLIMITED)
    private val scope = CoroutineScope(Dispatchers.IO + Job())

    private val lock = Any()  // 同步锁

    @Volatile
    private var running = false

    init {
        val accessoryList = usbManager.accessoryList
        if (accessoryList.isNullOrEmpty()) {
            onError("No accessory found")
        } else {
            openAccessory(accessoryList[0])
        }
    }

    fun send(payload: ByteArray) {
        scope.launch(Dispatchers.IO) {
            sendChannel.send(payload)
        }
    }

    private fun receive(payload: ByteArray, length: Int) {
        onReceive(payload, length)
    }

    fun openAccessory(accessory: UsbAccessory) {
        val data = ByteArray(64 * 1024) { 0xAA.toByte() }
        fileDescriptor = usbManager.openAccessory(accessory)
        if (fileDescriptor != null) {
            val fd: FileDescriptor = fileDescriptor!!.fileDescriptor
            inStream = FileInputStream(fd)
            outStream = FileOutputStream(fd)
            running = true

            // 接收协程
            scope.launch(Dispatchers.IO) {
                try {
                    val buffer = ByteArray(BUFFER_SIZE_IN_BYTES)
                    while (running) {
                        val len = synchronized(lock) {
                            inStream?.read(buffer) ?: -1
                        }
                        if (len > 0) {
                            send(data)
                            // receive(buffer, len)
                            // delay(DELAY)
                            // send(buffer.sliceArray(0..len))
                        } else break
                    }
                } catch (e: Exception) {
                    onError("USB Receive Failed: ${e.message}")
                    closeAccessory()
                }
            }

            // 发送协程
            scope.launch(Dispatchers.IO) {
                try {
                    for (msg in sendChannel) {
                        synchronized(lock) {
                            if (running) outStream?.write(msg)
                        }
                    }
                } catch (e: Exception) {
                    onError("USB Send Failed: ${e.message}")
                }
            }

            onConnected()
        } else {
            onError("Could not connect")
        }
    }

    fun closeAccessory() {
        synchronized(lock) {
            if (!running) return  // 避免重复关闭
            running = false
        }

        scope.launch {
            sendChannel.close()
        }

        // 再安全关闭流
        synchronized(lock) {
            try {
                inStream?.close()
                outStream?.close()
                fileDescriptor?.close()
            } catch (e: IOException) {
                onError("Close error: ${e.message}")
            } finally {
                inStream = null
                outStream = null
                fileDescriptor = null
            }
        }
        onDisconnected()
    }

    abstract fun onReceive(payload: ByteArray, length: Int)
    abstract fun onError(msg: String)
    abstract fun onConnected()
    abstract fun onDisconnected()

    companion object Constants {
        const val BUFFER_SIZE_IN_BYTES = 1024
        const val DELAY = 10L
    }
}